package com.irdstudio.apicenter.gateway.comm.http;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.HttpHostConnectException;
import org.apache.http.entity.ContentProducer;
import org.apache.http.entity.EntityTemplate;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.fasterxml.jackson.core.JsonParser.Feature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.util.BeanUtil;
import com.irdstudio.apicenter.gateway.comm.MaccsChannelClient;
import com.irdstudio.apicenter.gateway.comm.MaccsPacket;
import com.irdstudio.apicenter.gateway.comm.MaccsPacketUtil;
import com.irdstudio.apicenter.gateway.comm.pack.AbstracePackProcessor;
import com.irdstudio.apicenter.gateway.core.MaccsConstant;
import com.irdstudio.apicenter.gateway.core.dm.AppChannelForward;
import com.irdstudio.apicenter.gateway.util.spring.BeanUtils;
import com.irdstudio.apicenter.gateway.web.MaccsTradeDefineVo;
/**
 * 实现MACCS系统下的HTTP渠道通讯
 * 		根据指定的渠道定义信息进行初始化并进行通讯 
 * @author guangming.li
 * @version 1.0
 * @date 2015-05-22
 */
@Service(value="HTTP")
public class MaccsHttpClient implements MaccsChannelClient {
	
	/* 渠道定义信息 */
	private AppChannelForward acf = null;
	
	/* 文本日志记录对象(Logback对象) */
	private final static Logger logger = LoggerFactory
			.getLogger(MaccsHttpClient.class);
	
	/**
	 * 传入渠道定义信息(创建时传入)
	 */
	@Override
	public void setChannelDefine(AppChannelForward acf) {
		this.acf = acf;
	}

	@SuppressWarnings("unchecked")
	@Override
	public MaccsPacket sendAndWait(MaccsTradeDefineVo tradeDefine,
			MaccsPacket inUvo) {
			
		// 组装成完整的请求地址(渠道地址+交易代码)
		logger.info("HTTP渠道：" + getChannelDesc());
		MaccsPacket backPacket = null;
		//String connectURL = acf.getForwardChannelUrl();
		String connectURL ="http://127.0.0.1:8081/cbms/CMBCommController.do";
		// 1.准备交易输入数据,如果有配置输入格式化信息，则按格式化配置对数据进行格式化
		if (!(tradeDefine.getInList() == null
				|| tradeDefine.getInList().size() == 0)) {
			inUvo = MaccsPacketUtil.makeFormatPacket(tradeDefine.getInList(),inUvo);
		}		
		
		// 2.准备HTTP交易地址,如果渠道指定的交易码字段为空,则交易码直接附加在通讯地址后面
		if (StringUtils.isEmpty(acf.getChannelTradeCodeField())) {
			if (!connectURL.endsWith("/")) {
				connectURL += "/";
			}
			connectURL += tradeDefine.getTradeCode();
		} else {
			// 如果渠道通讯指定了交易码字段，则将交易码值放入该字段中一起以便一起发送过去
			inUvo.addVariable(acf.getChannelTradeCodeField(), tradeDefine
					.getTradeCode());
		}
		logger.info("HTTP交易：" + connectURL);
		logger.info("发送信息(原始):" + inUvo.toString());
		
		// 获取组/解包处理类
		AbstracePackProcessor packProcessor = (AbstracePackProcessor) BeanUtils
				.getBean(acf.getPackProcessorClass());
		
		CloseableHttpClient httpclient = HttpClients.createDefault();
		try {
			
			HttpPost httpPost = new HttpPost(connectURL);

			
			// 如果渠道指定用POST的方式提交,则作如下处理
			if (MaccsConstant.APP_HTTP_STREAM_TYPE_POST.equals(acf
					.getHttpStreamType())) {
				List<NameValuePair> nvps = new ArrayList<NameValuePair>();
				for (Iterator<?> iter = inUvo.getMap().keySet().iterator(); iter
						.hasNext();) {
					String key = (String) iter.next();
					String value = inUvo.getValue(key);
					nvps.add(new BasicNameValuePair(key, value));
				}
				httpPost.setEntity(new UrlEncodedFormEntity(nvps,acf.getChannelCharset()));
			} else {
				
				// 非使用POST提交情况下,均采用发送数据流的方式与其进行通讯
				httpPost.setHeader("Accept-Charset", acf.getChannelCharset());
				httpPost.setHeader("Accept-Language", "zh-cn");
				httpPost.setHeader("Content-Type", "text/html,charset="
						+ acf.getChannelCharset());	
				
				// 使用组/解包处理类来获得要发送的数据包
				final String msg = packProcessor.getSendPackage(acf,tradeDefine, inUvo);
				
				ContentProducer cp = new ContentProducer() {
					public void writeTo(OutputStream outstream)
							throws IOException {
						Writer wr = new OutputStreamWriter(outstream, acf.getChannelCharset());
						wr.write(msg);
						wr.flush();
					}
				};
				HttpEntity httpEntity = new EntityTemplate(cp);
				httpPost.setEntity(httpEntity);
			}
			
			// 处理返回结果
			CloseableHttpResponse response = httpclient.execute(httpPost);
			try {
				logger.info("响应状态：" + response.getStatusLine());
				if (response.getStatusLine().getStatusCode() == 200) {

					// 读取http返回的内容
					HttpEntity entity = response.getEntity();
					String result = EntityUtils.toString(entity, acf
							.getChannelCharset());
					EntityUtils.consume(entity);
					logger.info("响应内容(原始)：" + result);

					// 使用组/解包处理类将返回的数据处理成maccs所需要的数据包
					backPacket = packProcessor.getReadPackage(acf, tradeDefine, result);	
					
					// 如果有指定对返回内容进行格式化,则按格式化要求返回
					if (!(tradeDefine.getOutList() == null
							|| tradeDefine.getOutList().size() == 0)) {
						backPacket = MaccsPacketUtil.makeFormatPacket(tradeDefine.getOutList(),backPacket);
					}
					

				} else {
					logger.error("未接收到响应信息，调用失败!");
					backPacket = MaccsPacketUtil
							.makeFailurePacket("未接收到响应信息，调用失败!");
				}
			} finally {
				response.close();
			}
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			backPacket = MaccsPacketUtil.makeFailurePacket(e.getMessage());
		} catch (ClientProtocolException e) {
			e.printStackTrace();
			backPacket = MaccsPacketUtil.makeFailurePacket(e.getMessage());
		} catch (HttpHostConnectException e) {
			e.printStackTrace();
			backPacket = MaccsPacketUtil.makeFailurePacket("连接拒绝：" + connectURL
					+ "," + e.getMessage());
		} catch (IOException e) {
			e.printStackTrace();
			backPacket = MaccsPacketUtil.makeFailurePacket(e.getMessage());
		} finally {
			try {
				httpclient.close();
			} catch (IOException e) {

				e.printStackTrace();
			}
		}
		return backPacket;
	}

	/**
	 * 返回渠道信息描述
	 * @return
	 */
	private String getChannelDesc() {
		StringBuffer sb = new StringBuffer();
		sb.append(acf.getForwardChannelName()).append("(");
		sb.append(acf.getForwardChannelId()).append(")");
		sb.append(",通讯地址:").append(acf.getForwardChannelUrl());
		sb.append(",字符编码:").append(acf.getChannelCharset());
		return sb.toString();
	}	
}
